using System;
using System.Collections.Generic;
using System.Net;
using SpoonCore.Message.Info.Core;
using SpoonCore.Routing;

namespace SpoonCore
{
	public class SpoonClient : IComparable<SpoonClient>, IDisposable
	{
		private SpoonSocket _socket;
		private MessageHandler _msgHandler;
		private List<IPEndPoint> _bootstraps = new List<IPEndPoint>();
		private SpoonIdentifier _id = new SpoonIdentifier();
		private HandlerManager _handlerManager;

		private RoutingTable _routingTable;
		private LeaveTable _leaveTable;

		public delegate void OnStartHandler();

		public event OnStartHandler OnStart;

		public SpoonClient()
			: this(0)
		{
		}

		public SpoonClient(int port)
		{
			_routingTable = new RoutingTable(this);
			_leaveTable = new LeaveTable(this);

			_msgHandler = new MessageHandler(this);
			_socket = new SpoonSocket(port);
			_socket.OnDataReceive += new MessageHandler.OnDataReceiveHandler(_msgHandler.ReceiveMessage);

			_handlerManager = new HandlerManager(this, _msgHandler);
			_handlerManager.LoadHandler();
		}

		public List<IPEndPoint> Bootstraps
		{
			get { return _bootstraps; }
		}

		public SpoonIdentifier Id
		{
			get { return _id; }
		}

		public RoutingTable RoutingTable
		{
			get { return _routingTable; }
		}

		public LeaveTable LeaveTable
		{
			get { return _leaveTable; }
			set { _leaveTable = value; }
		}

		public void Start()
		{
			if (OnStart != null) OnStart();
		}

		public void AddBootstrap(IPEndPoint point)
		{
			Bootstraps.Add(point);
		}

		public void Send(SpoonIdentifier id, IMessage message)
		{
			ClientInfo dest = RoutingTable.FindNext(id);
			Send(dest, message);
		}

		public void Send(ClientInfo info, IMessage message)
		{
			if (info != null && info.Endpoints.Length > 0)
			{
				IPEndPoint dest = info.Endpoints[0];
				Send(dest, message);
			}
		}

		public void Send(IPEndPoint point, IMessage message)
		{
			_socket.Send(point, _msgHandler.GetBytes(message, point));
		}

		public int CompareTo(SpoonClient other)
		{
			return _id.CompareTo(other.Id);
		}

		public override string ToString()
		{
			return _id.ToString();
		}

		public void AddClient(ClientInfo client)
		{
			_routingTable.Add(client);
			_leaveTable.Add(client);
			
		}

		public void AddClient(IList<ClientInfo> clients)
		{
			foreach (ClientInfo info in clients)
			{
				AddClient(info);
			}
		}

		public void Dispose()
		{
			_socket.Dispose();
			_handlerManager.Dispose();
		}

		public void RemoveClient(IPEndPoint destination)
		{
			List<ClientInfo> res = new List<ClientInfo>();
			foreach (ClientInfo info in _routingTable.Clients)
			{
				foreach (IPEndPoint point in info.Endpoints)
				{
					if (point == destination)
					{
						res.Add(info);
						break;
					}
				}
			}
			foreach (ClientInfo info in _routingTable.Clients)
			{
				foreach (IPEndPoint point in info.Endpoints)
				{
					if (point == destination)
					{
						res.Add(info);
						break;
					}
				}
			}

			foreach (ClientInfo info in res)
			{
				RemoveClient(info);
			}
		}

		public void RemoveClient(ClientInfo client)
		{
			// Console.WriteLine(_id + ": remove " + client.Id);
			_routingTable.Remove(client);
			_leaveTable.Remove(client);
		}

		public bool Check(List<SpoonClient> clients)
		{
			bool status = true;
			if (!_routingTable.Check(clients)) status = false;
			if (!_leaveTable.Check(clients)) status = false;
			return status;
		}
	}
}